[AWS, CoffeeScript]”AWS SDK”をちょっと便利に使いたい【Node.js】
Nodeで AWS SDK を使っていてもう少し便利だと良いのになと思ったので、どうしたら便利になるかを考えてみました。
まずは誰もが使って分かりやすそうな「S3」を対象にしてみます。
ちなみに「CoffeeScript」を利用しています。
S3
バケットの作成 - (AWS.Request) createBucket(params = {}, callback)
そのまま利用すると以下のようになります。
s3.createBucket params, (err, data) -> if err console.log err else console.log data
単純に作成するだけなら別に問題はないのですが、作成しようとしたバケット名が既にある時には以下のようなエラーが発生してしまいます。
- BucketAlreadyExists
- BucketAlreadyOwnedByYou
バケットが存在しない時だけに作成できればいいのにと思うので、バケットが存在しない時にだけバケット作成を行うようにしてみます。
create_bucket = (bucket, callback, params = {}) -> params.Bucket = bucket if bucket? params.Key = key if key? head_bucket bucket, (err, data) => if err?.statusCode is 404 s3.createBucket params, (err, data) -> console.log "create_bucket" callback err, data else callback err, data head_bucket = (bucket, callback) -> params = {} params.Bucket = bucket if bucket? s3.headBucket params, (err, data) => console.log "headBucket" callback err, data
「headBucket」メソッドを利用することでバケットの存在チェックを行うことができます。
(AWS.Request) headBucket(params = {}, callback)
エラーの場合
"statusCode"が "404"だったらバケットが存在しない。
"statusCode"が "301"だったらバケットが存在する(他人が作成したバケット)。
正常の場合
バケットが存在する(自分が作成したバケット)。
よって、"statusCode"が "404"の時だけバケットを作成するようにしてみました。
"statusCode"が "301"の時にはそのままエラーとして、正常だったら何もしないのが良いかなと。
「createBucket」メソッドには他にもいろいろオプションはありますが、指定できるようにだけしておきます。
(何か便利な指定の仕方ありませんかね)
バケットの削除 - (AWS.Request) deleteBucketCors(params = {}, callback)
そのまま利用すると以下のようになります。
s3.deleteBucket params, (err, data) -> if err console.log err else console.log data
バケットの作成と同様に自分のバケットがある時だけ削除するようにしてみます。
delete_bucket = (bucket, callback) -> params = {} params.Bucket = bucket if bucket? head_bucket bucket, (err, data) => if err?.statusCode is 404 callback null, data else if err?.statusCode is 403 callback err, data else if err?.statusCode is 301 callback err, data else s3.deleteBucket params, (err, data) -> console.log "delete_bucket" callback err, data head_bucket = (bucket, callback) -> params = {} params.Bucket = bucket if bucket? s3.headBucket params, (err, data) => console.log "headBucket" callback err, data
同じく「headBucket」メソッドを利用してバケットの存在チェックを行なって、正常の時のみ削除するようにしてみました。
バケットの削除は、バケット内に一つでもオブジェクトがあると削除できないも不便ですね。
オブジェクトをすべて削除してからバケットを削除できるようなメソッドも作ってみました。
delete_bucket_force = (bucket, callback) -> head_bucket bucket, (err, data) => if err?.statusCode is 404 callback null, data else if err?.statusCode is 403 callback err, data else list_objects bucket, (err, data) => if err callback err, data else console.log data.Contents unless data.Contents.length is 0 delete_objects bucket, data.Contents, (err, data) => if err callback err, data else params = {} params.Bucket = bucket if bucket? s3.deleteBucket params, (err, data) -> console.log "delete_bucket_force" callback err, data else params = {} params.Bucket = bucket if bucket? s3.deleteBucket params, (err, data) -> console.log "delete_bucket_force" callback err, data head_bucket = (bucket, callback) -> params = {} params.Bucket = bucket if bucket? s3.headBucket params, (err, data) => console.log "headBucket" callback err, data list_objects = (bucket, callback, params = {}) -> params.Bucket = bucket if bucket? s3.listObjects params, (err, data) => console.log "list_objects" console.log data.Contents if data? callback err, data delete_objects = (bucket, deletes, callback, params = {}) -> params.Bucket = bucket if bucket? keys = [] for key in deletes keys.push { Key: key.Key } params.Delete = { Objects: keys } s3.deleteObjects params, (err, data) -> console.log "delete_objects" callback err, data
「listObjects」メソッドでオブジェクトの一覧を取得して、「deleteObjects」で取得した一覧すべてを削除しています。
クラス化
バケットの作成と削除はちょっと便利になった気がしますが、その他のメソッドはそのままでもそんなに問題ありませんでした。
そこでせっかく「CoffeeScript」を利用しているので、クラス化して便利に使えるようにしてみます。
(全メソッドは用意していませんが、今回紹介した以外のメソッドも用意していたりもします)
class S3 AWS = require 'aws-sdk' path = require 'path' constructor: (file) -> AWS.config.loadFromPath path.normalize file @s3 = new AWS.S3() create_bucket: (bucket, callback, params = {}) -> params = @create_params bucket, null, params @head_bucket bucket, (err, data) => if err?.statusCode is 404 @s3.createBucket params, (err, data) -> console.log "create_bucket" callback err, data else callback err, data delete_bucket: (bucket, callback) -> params = @create_params bucket, null @head_bucket bucket, (err, data) => if err?.statusCode is 404 callback null, data else if err?.statusCode is 403 callback err, data else if err?.statusCode is 301 callback err, data else @s3.deleteBucket params, (err, data) -> console.log "delete_bucket" callback err, data delete_bucket_force: (bucket, callback) -> @head_bucket bucket, (err, data) => if err?.statusCode is 404 callback null, data else if err?.statusCode is 403 callback err, data else @list_objects bucket, (err, data) => if err callback err, data else unless data.Contents.length is 0 @delete_objects bucket, data.Contents, (err, data) => if err callback err, data else params = @create_params bucket, null @s3.deleteBucket params, (err, data) -> console.log "delete_bucket_force" callback err, data else params = @create_params bucket, null @s3.deleteBucket params, (err, data) -> console.log "delete_bucket_force" callback err, data delete_object: (bucket, key, callback) -> params = @create_params bucket, key @head_object bucket, key, (err, data) => if err?.statusCode is 404 callback null, data else if err?.statusCode is 403 callback err, data else @s3.deleteObject params, (err, data) -> console.log "delete_object" callback err, data delete_objects: (bucket, deletes, callback, params = {}) -> params = @create_params bucket, null, params keys = [] for key in deletes keys.push { Key: key.Key } params.Delete = { Objects: keys } @s3.deleteObjects params, (err, data) -> console.log "delete_objects" callback err, data getBucketVersioning: (bucket, callback) -> params = @create_params bucket, null @s3.getBucketVersioning params, (err, data) => console.log "getBucketVersioning" callback err, data get_acl: (params, callback) -> @s3.getBucketAcl params, (err, data) -> console.log "get_acl" console.log data.Grants if data? callback err, data get_object: (params, callback) -> @s3.getObject params, (err, data) -> console.log "get_object" callback err, data head_bucket: (bucket, callback) -> params = @create_params bucket, null @s3.headBucket params, (err, data) => console.log "headBucket" callback err, data head_object: (bucket, key, callback) -> params = @create_params bucket, key @s3.headObject params, (err, data) => console.log "head_object" callback err, data put_object: (bucket, key, callback, params = {}) -> params = @create_params bucket, key, params @s3.putObject params, (err, data) -> console.log "put_object" callback err, data list_buckets: (callback) -> @s3.listBuckets null, (err, data) -> console.log "list_buckets" console.log data.Buckets if data? callback err, data list_objects: (bucket, callback, params = {}) -> params = @create_params bucket, null, params @s3.listObjects params, (err, data) => console.log "list_objects" console.log data.Contents if data? callback err, data create_params: (bucket, key, params = {}) -> params.Bucket = bucket if bucket? params.Key = key if key? return params module.exports = S3
利用例
async = require 'async' S3 = require './S3' s3 = new S3('../config/config.json') bucket_name = "classmethod-hoge" key_name = "" delete_bucket_force = (callback) -> s3.delete_bucket_force bucket_name, callback delete_bucket = (callback) -> s3.delete_bucket bucket_name, callback create_bucket = (callback) -> params = ACL: "public-read" CreateBucketConfiguration: { LocationConstraint: "ap-northeast-1" } s3.create_bucket bucket_name, callback, params put_object = (callback) -> params = Body: "Hello world!" key_name = "Key" s3.put_object bucket_name, key_name, callback, params get_object = (callback) -> s3.get_object bucket_name, key_name, callback, params delete_object = (callback) -> s3.delete_object bucket_name, key_name, callback list_buckets = (callback) -> s3.list_buckets callback list_objects = (callback) -> s3.list_objects bucket_name, callback tasks = [] tasks.push list_buckets tasks.push create_bucket tasks.push put_object tasks.push delete_object tasks.push delete_bucket async.series tasks, (err, results) -> if err console.log err else console.log results
パラメータをもう少し便利に使う方法があれば、もっと良くなる気がするんですが・・・
なお、今回のソースはgithubに公開してあります
https://github.com/jun116/aws-sdk-coffee